package com.bowlong.text;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.bowlong.objpool.StringBuilderPool;

@SuppressWarnings({ "rawtypes", "unchecked" })
public class EasyTemplate {
	private class Cache {
		public byte[] b;
		public long lastModified;
	}

	public static final Map<String, Cache> caches = new Hashtable<String, Cache>();

	public static EasyTemplate _template = new EasyTemplate();

	public Cache newCache() {
		return new Cache();
	}

	public static final String make(File file, Map<String, String> params,
			String encode) throws Exception {
		byte[] b = readFully(file);
		String s = new String(b, encode);
		return make(s, params);
	}

	public static final String make2(File file, Map<String, String> params,
			String encode) throws Exception {
		String fname = file.getPath();

		byte[] b;
		if (caches.containsKey(fname)) {
			Cache c = caches.get(fname);
			if (c == null || c.lastModified < file.lastModified()) {
				c = _template.newCache();
				b = readFully(file);
				c.b = b;
				c.lastModified = file.lastModified();
				caches.put(fname, c);
			} else {
				b = c.b;
			}
		} else {
			Cache c = _template.newCache();
			b = readFully(file);
			c.b = b;
			c.lastModified = file.lastModified();
			caches.put(fname, c);
		}

		String s = new String(b, encode);
		return make(s, params);
	}

	public static final String make(String s, Map params) throws Exception {
		if (s == null || s.isEmpty() || params == null || params.isEmpty())
			return s;
		if (s.indexOf("${") < 0 && s.indexOf("$[") < 0)
			return s;
		StringBuilder sb = StringBuilderPool.borrowObject();
		sb.append(s);
		// StringBuilder sb = new StringBuilder(s);
		try {
			Set<Entry> entrys = params.entrySet();
			for (Entry e : entrys) {
				Object key = e.getKey();
				Object v = e.getValue();
				String k = "${" + key + "}";
				String k2 = "$[" + key + "]";
				String var = String.valueOf(v);

				int i1 = sb.indexOf(k);
				while (i1 >= 0) {
					int end = i1 + k.length();
					sb.replace(i1, end, var);
					i1 = sb.indexOf(k);
				}

				int i2 = sb.indexOf(k2);
				while (i2 >= 0) {
					int end = i2 + k2.length();
					sb.replace(i2, end, "\"" + var + "\"");
					i2 = sb.indexOf(k2);
				}

				// while (s.contains(k))
				// s = s.replace(k, var);
				// while (s.contains(k2))
				// s = s.replace(k2, "\"" + var + "\"");
			}
			return sb.toString();
		} finally {
			StringBuilderPool.returnObject(sb);
		}
	}

	public static final byte[] readFully(File f) throws Exception {
		if (f == null || !f.exists()) {
			throw new IOException("file no exists");
		}
		int len = (int) f.length();
		byte[] b = new byte[len];
		try (FileInputStream fis = new FileInputStream(f);
				DataInputStream dis = new DataInputStream(fis);) {
			dis.readFully(b);
			dis.close();
			fis.close();
			return b;
		}
	}

	public static final Map<String, String> newMap() {
		return new HashMap<String, String>();
	}

	public static final String toStringFormat(final String str) {
		if (str == null || str.length() <= 0)
			return str;
		int start1 = str.indexOf(OPEN1);
		StringBuilder sb = new StringBuilder(str);
		while (start1 >= 0) {
			start1 = start1 + 2;
			int end = sb.indexOf(CLOSE1, start1);
			if (end < 0)
				break;

			sb.replace(start1, end, "%s");
			start1 = sb.indexOf(OPEN1, start1);
		}
		int start2 = sb.indexOf(OPEN2);
		while (start2 >= 0) {
			int end2 = sb.indexOf(CLOSE2, start2);
			if (end2 < 0)
				break;

			sb.replace(start2, end2 + 1, "\"${%s}\"");
			start2 = sb.indexOf(OPEN2, start2);
		}
		return sb.toString();
	}

	public static final String toStringFormat2(final String str) {
		if (str == null || str.length() <= 0)
			return str;
		int start1 = str.indexOf(OPEN1);
		int p = 1;
		StringBuilder sb = new StringBuilder(str);
		while (start1 >= 0) {
			start1 = start1 + 2;
			int end = sb.indexOf(CLOSE1, start1);
			if (end < 0)
				break;

			sb.replace(start1, end, Integer.toString(p++));
			start1 = sb.indexOf(OPEN1, start1);
		}
		int start2 = sb.indexOf(OPEN2);
		while (start2 >= 0) {
			int end2 = sb.indexOf(CLOSE2, start2);
			if (end2 < 0)
				break;

			sb.replace(start2, end2 + 1, String.format("\"${%d}\"", p++));
			start2 = sb.indexOf(OPEN2, start2);
		}

		return sb.toString();
	}

	static final int MAX_RUN = 100;

	public static final String format(final String fmt, final Object... args) {
		if (fmt == null || fmt.length() <= 0)
			return fmt;
		int run = 0;
		int i = 0;
		StringBuilder sb = new StringBuilder(fmt);
		int start1, start2, end1, end2;
		start1 = start2 = end1 = end2 = -1;
		String newval;

		boolean r1 = false;
		boolean r2 = false;
		while (true) {
			if (run++ > MAX_RUN)
				return sb.toString();

			start1 = sb.indexOf(OPEN1, end1);
			end1 = sb.indexOf(CLOSE1, start1);
			if (start1 >= 0 && end1 >= 0) {
				newval = String.valueOf(args[i++]);
				sb.replace(start1, end1 + 1, newval);
			} else
				r1 = true;

			start2 = sb.indexOf(OPEN2, end2);
			end2 = sb.indexOf(CLOSE2, start2);
			if (start2 >= 0 && end2 >= 0) {
				newval = "\"" + String.valueOf(args[i++]) + "\"";
				sb.replace(start2, end2 + 1, newval);
			} else
				r2 = true;

			if (r1 && r2)
				break;
		}
		return sb.toString();
	}

	public static final String OPEN1 = "${";
	public static final String CLOSE1 = "}";

	public static final String OPEN2 = "$[";
	public static final String CLOSE2 = "]";

	public static void main(String[] args) throws Exception {
		String fmt = "aaaabbbbccc1 ${abc.name} [你好] 1cccc $[a] ${{";
		String str2 = format(fmt, "脏三", "ccc");
		System.out.println(str2);
		// String str = EasyTemplate.make(
		// fmt,
		// MapEx.builder().put("abc.name", "xxxxxxxxx")
		// .put("a", "__%[x够x]__").toMap());
		// System.out.println(str);
	}
}
